package com.docmgmt.mvp.service;

import com.docmgmt.mvp.dto.FileUploadResponse;
import com.docmgmt.mvp.dto.FileVO;
import com.docmgmt.mvp.dto.PagedResult;
import com.docmgmt.mvp.entity.FileEntity;
import com.docmgmt.mvp.exception.BusinessException;
import com.docmgmt.mvp.mapper.FileMapper;
import com.docmgmt.mvp.ragflow.RAGFlowClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 文件服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FileService {

    private final FileStorageService fileStorageService;
    private final RAGFlowClient ragFlowClient;
    private final KnowledgeBaseService knowledgeBaseService;
    private final FileMapper fileMapper;

    /**
     * 上传文件并同步到 RAGFlow
     *
     * 缓存清理：文件上传后清理所有用户的权限缓存
     *
     * @param file 上传的文件
     * @param userId 用户 ID
     * @return 上传响应
     */
    @CacheEvict(value = "user_accessible_docs", allEntries = true)
    public FileUploadResponse uploadFile(MultipartFile file, Long userId) {
        log.info("开始处理文件上传: filename={}, size={}, userId={}",
                file.getOriginalFilename(), file.getSize(), userId);

        try {
            // 1. 上传文件到 MinIO
            String storagePath = fileStorageService.uploadFile(file);
            log.info("文件已上传到 MinIO: {}", storagePath);

            // 2. 转换为临时文件用于上传到 RAGFlow
            File tempFile = convertToFile(file);

            // 3. 上传到 RAGFlow
            String ragflowDocId = null;
            String syncStatus = "pending";
            String kbId = null;

            try {
                // 获取或创建默认知识库
                kbId = knowledgeBaseService.getOrCreateDefaultKnowledgeBase();
                log.info("使用知识库 ID: {}", kbId);

                // 上传文档到 RAGFlow
                ragflowDocId = ragFlowClient.uploadDocument(kbId, tempFile);
                syncStatus = "success";
                log.info("文件已同步到 RAGFlow: docId={}, kbId={}", ragflowDocId, kbId);

            } catch (Exception e) {
                log.error("同步到 RAGFlow 失败", e);
                syncStatus = "failed";
                // 不抛出异常，允许文件继续保存到 MinIO
            } finally {
                // 删除临时文件
                if (tempFile != null && tempFile.exists()) {
                    boolean deleted = tempFile.delete();
                    if (!deleted) {
                        log.warn("临时文件删除失败: {}", tempFile.getAbsolutePath());
                    }
                }
            }

            // 4. 保存文件元数据到数据库
            LocalDateTime now = LocalDateTime.now();
            FileEntity fileEntity = FileEntity.builder()
                    .fileName(file.getOriginalFilename())
                    .folderId(0L)  // 路径A简化：暂时都放在根目录（ID=0）
                    .storagePath(storagePath)
                    .fileExtension(getFileExtension(file.getOriginalFilename()))
                    .fileSize(file.getSize())
                    .mimeType(file.getContentType())
                    .versionNumber(1)
                    .uploadedBy(userId != null ? userId : 1L)
                    .downloadCount(0)
                    .viewCount(0)
                    .isLocked(false)
                    .deleted(false)
                    .createdAt(now)
                    .updatedAt(now)
                    .build();

            fileMapper.insert(fileEntity);
            log.info("文件元数据已保存到数据库: id={}, name={}", fileEntity.getId(), fileEntity.getFileName());

            // 5. 构造响应
            return FileUploadResponse.builder()
                    .fileId(fileEntity.getId())  // 使用真实的数据库ID
                    .fileName(file.getOriginalFilename())
                    .fileSize(file.getSize())
                    .fileType(file.getContentType())
                    .storagePath(storagePath)
                    .ragflowDocId(ragflowDocId)
                    .ragflowKbId(kbId)
                    .uploadTime(fileEntity.getCreatedAt() != null ?
                            fileEntity.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) :
                            LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME))
                    .syncStatus(syncStatus)
                    .build();

        } catch (Exception e) {
            log.error("文件上传失败", e);
            throw new BusinessException("文件上传失败: " + e.getMessage(), e);
        }
    }

    /**
     * 查询文件列表（路径A简化版：只查询用户自己上传的文件）
     *
     * @param userId    用户ID
     * @param folderId  文件夹ID（可选）
     * @param page      页码
     * @param pageSize  每页数量
     * @param sortBy    排序字段
     * @param sortOrder 排序方向
     * @return 分页文件列表
     */
    public PagedResult<FileVO> listFiles(
            Long userId,
            Long folderId,
            Integer page,
            Integer pageSize,
            String sortBy,
            String sortOrder
    ) {
        log.info("查询文件列表: userId={}, folderId={}, page={}, pageSize={}",
                userId, folderId, page, pageSize);

        // 参数默认值
        if (page == null || page < 1) page = 1;
        if (pageSize == null || pageSize < 1) pageSize = 20;
        if (sortBy == null || sortBy.isEmpty()) sortBy = "created_at";
        if (sortOrder == null || !sortOrder.equalsIgnoreCase("asc")) sortOrder = "desc";

        // 查询数据库
        List<FileEntity> entities = fileMapper.listFilesByCreator(
                userId,
                folderId,
                sortBy,
                sortOrder
        );

        // 手动分页（路径A简化，路径B应该用MyBatis Plus的分页插件）
        int total = entities.size();
        int start = (page - 1) * pageSize;
        int end = Math.min(start + pageSize, total);
        List<FileEntity> pagedEntities = (start < total) ?
                entities.subList(start, end) : List.of();

        // 转换为VO
        List<FileVO> fileVOs = pagedEntities.stream()
                .map(this::entityToVO)
                .collect(Collectors.toList());

        // 构造分页响应
        return PagedResult.<FileVO>builder()
                .items(fileVOs)
                .pagination(PagedResult.Pagination.builder()
                        .page(page)
                        .pageSize(pageSize)
                        .total((long) total)
                        .totalPages((int) Math.ceil((double) total / pageSize))
                        .build())
                .build();
    }

    /**
     * 根据ID查询文件
     *
     * @param fileId 文件ID
     * @return 文件实体
     */
    public FileEntity getFileById(Long fileId) {
        FileEntity file = fileMapper.selectById(fileId);
        if (file == null || file.getIsDeleted()) {
            throw new BusinessException("文件不存在或已删除");
        }
        return file;
    }

    /**
     * 获取文件详情（增强版，包含更多元数据）
     *
     * @param fileId 文件ID
     * @return 文件VO
     */
    public FileVO getFileDetail(Long fileId) {
        log.info("获取文件详情: fileId={}", fileId);

        FileEntity file = getFileById(fileId);

        // 转换为VO
        FileVO vo = entityToVO(file);

        // 添加额外信息
        vo.setViewCount(file.getViewCount() != null ? file.getViewCount() : 0);
        vo.setDownloadCount(file.getDownloadCount() != null ? file.getDownloadCount() : 0);
        vo.setIsLocked(file.getIsLocked() != null ? file.getIsLocked() : false);
        vo.setStorageKey(file.getStoragePath());

        log.info("获取文件详情成功: fileId={}, name={}", fileId, file.getFileName());
        return vo;
    }

    /**
     * 删除文件（软删除）
     *
     * 缓存清理：文件删除后清理所有用户的权限缓存
     *
     * @param fileId 文件ID
     * @param userId 操作用户ID
     */
    @CacheEvict(value = "user_accessible_docs", allEntries = true)
    public void deleteFile(Long fileId, Long userId) {
        log.info("删除文件: fileId={}, userId={}", fileId, userId);

        FileEntity file = getFileById(fileId);

        // 使用MyBatis-Plus的逻辑删除（通过@TableLogic注解自动处理）
        fileMapper.deleteById(fileId);

        log.info("文件删除成功: fileId={}", fileId);

        // TODO: 触发从RAGFlow删除（如果文件不再属于任何知识库）
        // 这个逻辑应该在 KnowledgeBaseService 中处理
    }

    /**
     * 生成文件下载URL
     *
     * @param fileId 文件ID
     * @return 下载URL
     */
    public String generateDownloadUrl(Long fileId) {
        log.info("生成下载URL: fileId={}", fileId);

        FileEntity file = getFileById(fileId);

        // 从MinIO生成预签名URL（有效期1小时）
        String downloadUrl = fileStorageService.generatePresignedUrl(file.getStoragePath(), 3600);

        // 增加下载计数
        file.setDownloadCount((file.getDownloadCount() != null ? file.getDownloadCount() : 0) + 1);
        file.setUpdatedAt(LocalDateTime.now());
        fileMapper.updateById(file);

        log.info("下载URL已生成: fileId={}, downloadCount={}", fileId, file.getDownloadCount());
        return downloadUrl;
    }

    /**
     * 获取文件预览URL
     *
     * @param fileId 文件ID
     * @return 预览URL
     */
    public String getFilePreview(Long fileId) {
        log.info("获取文件预览: fileId={}", fileId);

        FileEntity file = getFileById(fileId);

        // 检查文件是否支持预览
        if (!isSupportPreview(file.getFileExtension())) {
            throw new BusinessException("该文件类型不支持预览");
        }

        // 从MinIO生成预签名URL（用于预览，有效期30分钟）
        String previewUrl = fileStorageService.generatePresignedUrl(file.getStoragePath(), 1800);

        // 增加浏览计数
        file.setViewCount((file.getViewCount() != null ? file.getViewCount() : 0) + 1);
        file.setUpdatedAt(LocalDateTime.now());
        fileMapper.updateById(file);

        log.info("预览URL已生成: fileId={}, viewCount={}", fileId, file.getViewCount());
        return previewUrl;
    }

    /**
     * 更新文件元数据
     *
     * 缓存清理：文件元数据更新后清理所有用户的权限缓存
     *
     * @param fileId   文件ID
     * @param newName  新文件名（可选）
     * @param folderId 新文件夹ID（可选）
     */
    @CacheEvict(value = "user_accessible_docs", allEntries = true)
    public void updateFileMetadata(Long fileId, String newName, Long folderId) {
        log.info("更新文件元数据: fileId={}, newName={}, folderId={}", fileId, newName, folderId);

        FileEntity file = getFileById(fileId);

        boolean updated = false;

        if (newName != null && !newName.trim().isEmpty() && !newName.equals(file.getFileName())) {
            file.setFileName(newName.trim());
            file.setFileExtension(getFileExtension(newName));
            updated = true;
        }

        if (folderId != null && !folderId.equals(file.getFolderId())) {
            file.setFolderId(folderId);
            updated = true;
        }

        if (updated) {
            file.setUpdatedAt(LocalDateTime.now());
            fileMapper.updateById(file);
            log.info("文件元数据已更新: fileId={}", fileId);
        } else {
            log.info("无需更新文件元数据: fileId={}", fileId);
        }
    }

    /**
     * 锁定/解锁文件
     *
     * @param fileId 文件ID
     * @param locked 是否锁定
     */
    public void lockFile(Long fileId, boolean locked) {
        log.info("{}文件: fileId={}", locked ? "锁定" : "解锁", fileId);

        FileEntity file = getFileById(fileId);
        file.setIsLocked(locked);
        file.setUpdatedAt(LocalDateTime.now());
        fileMapper.updateById(file);

        log.info("文件{}成功: fileId={}", locked ? "锁定" : "解锁", fileId);
    }

    /**
     * 批量删除文件
     *
     * @param fileIds 文件ID列表
     * @param userId  操作用户ID
     */
    public void batchDeleteFiles(List<Long> fileIds, Long userId) {
        log.info("批量删除文件: count={}, userId={}", fileIds.size(), userId);

        for (Long fileId : fileIds) {
            try {
                deleteFile(fileId, userId);
            } catch (Exception e) {
                log.error("删除文件失败: fileId={}, error={}", fileId, e.getMessage());
                // 继续处理下一个文件
            }
        }

        log.info("批量删除文件完成: total={}", fileIds.size());
    }

    /**
     * 搜索文件
     *
     * @param keyword  搜索关键词
     * @param userId   用户ID
     * @param page     页码
     * @param pageSize 每页大小
     * @return 分页结果
     */
    public PagedResult<FileVO> searchFiles(String keyword, Long userId, Integer page, Integer pageSize) {
        log.info("搜索文件: keyword={}, userId={}, page={}, pageSize={}", keyword, userId, page, pageSize);

        // 参数默认值
        if (page == null || page < 1) page = 1;
        if (pageSize == null || pageSize < 1) pageSize = 20;

        // 查询数据库（按文件名搜索）
        List<FileEntity> entities = fileMapper.searchByName(keyword, userId);

        // 手动分页
        int total = entities.size();
        int start = (page - 1) * pageSize;
        int end = Math.min(start + pageSize, total);
        List<FileEntity> pagedEntities = (start < total) ?
                entities.subList(start, end) : List.of();

        // 转换为VO
        List<FileVO> fileVOs = pagedEntities.stream()
                .map(this::entityToVO)
                .collect(Collectors.toList());

        // 构造分页响应
        return PagedResult.<FileVO>builder()
                .items(fileVOs)
                .pagination(PagedResult.Pagination.builder()
                        .page(page)
                        .pageSize(pageSize)
                        .total((long) total)
                        .totalPages((int) Math.ceil((double) total / pageSize))
                        .build())
                .build();
    }

    /**
     * 将 MultipartFile 转换为 File
     */
    private File convertToFile(MultipartFile multipartFile) throws Exception {
        String originalFilename = multipartFile.getOriginalFilename();
        File file = new File(System.getProperty("java.io.tmpdir") + "/" + originalFilename);

        try (FileOutputStream fos = new FileOutputStream(file)) {
            fos.write(multipartFile.getBytes());
        }

        return file;
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (filename == null || filename.isEmpty()) {
            return "";
        }
        int lastDotIndex = filename.lastIndexOf('.');
        return (lastDotIndex > 0 && lastDotIndex < filename.length() - 1) ?
                filename.substring(lastDotIndex + 1).toLowerCase() : "";
    }

    /**
     * 将实体转换为VO
     */
    private FileVO entityToVO(FileEntity entity) {
        return FileVO.builder()
                .id(entity.getId())
                .name(entity.getFileName())
                .folderId(entity.getFolderId())
                .folderPath(entity.getFilePath())
                .size(entity.getFileSize())
                .sizeDisplay(formatFileSize(entity.getFileSize()))
                .extension(entity.getFileExtension())
                .mimeType(entity.getMimeType())
                .hasPreview(isSupportPreview(entity.getFileExtension()))
                .previewUrl("/api/v1/files/" + entity.getId() + "/preview")
                .downloadUrl("/api/v1/files/" + entity.getId() + "/download")
                .uploadedBy(entity.getUploadedBy())
                .uploadedByName("未知用户")  // 路径A简化，路径B需要关联sys_user表
                .createdAt(entity.getCreatedAt() != null ?
                        entity.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null)
                .updatedAt(entity.getUpdatedAt() != null ?
                        entity.getUpdatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : null)
                .build();
    }

    /**
     * 格式化文件大小
     */
    private String formatFileSize(Long bytes) {
        if (bytes == null || bytes < 0) return "0 B";
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
        if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024));
        return String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024));
    }

    /**
     * 判断文件是否支持预览
     */
    private boolean isSupportPreview(String extension) {
        if (extension == null) return false;
        return List.of("pdf", "jpg", "jpeg", "png", "gif", "txt", "md").contains(extension.toLowerCase());
    }

    // ==================== v3.0新增方法 ====================

    /**
     * 秒传检查（通过MD5查重）
     *
     * @param md5      文件MD5
     * @param size     文件大小
     * @param userId   用户ID
     * @param folderId 目标文件夹ID
     * @return 检查结果VO
     */
    public com.docmgmt.mvp.dto.FileUploadCheckResponse checkFileUpload(
            String md5, Long size, Long userId, Long folderId) {
        log.info("秒传检查: md5={}, size={}, userId={}, folderId={}", md5, size, userId, folderId);

        // 根据MD5查询文件
        FileEntity existingFile = fileMapper.selectByMd5(md5);

        if (existingFile == null || !existingFile.getFileSize().equals(size)) {
            // 文件不存在或大小不匹配，需要上传
            return com.docmgmt.mvp.dto.FileUploadCheckResponse.builder()
                    .canSkipUpload(false)
                    .build();
        }

        // 文件已存在且大小匹配，返回文件信息
        FileVO fileVO = entityToVO(existingFile);

        log.info("秒传命中: fileId={}, name={}", existingFile.getId(), existingFile.getFileName());

        return com.docmgmt.mvp.dto.FileUploadCheckResponse.builder()
                .canSkipUpload(true)
                .file(fileVO)
                .build();
    }

    /**
     * 重命名文件
     *
     * @param fileId  文件ID
     * @param newName 新文件名
     * @param userId  用户ID
     * @return 更新后的文件VO
     */
    public FileVO renameFile(Long fileId, String newName, Long userId) {
        log.info("重命名文件: fileId={}, newName={}, userId={}", fileId, newName, userId);

        FileEntity file = getFileById(fileId);

        // 更新文件名
        file.setName(newName);
        file.setExtension(getFileExtension(newName));
        file.setUpdatedAt(LocalDateTime.now());
        fileMapper.updateById(file);

        log.info("文件重命名成功: fileId={}, newName={}", fileId, newName);

        return entityToVO(file);
    }

    /**
     * 移动文件
     *
     * 缓存清理：文件移动后清理所有用户的权限缓存
     *
     * @param fileId         文件ID
     * @param targetFolderId 目标文件夹ID
     * @param userId         用户ID
     * @return 更新后的文件VO
     */
    @CacheEvict(value = "user_accessible_docs", allEntries = true)
    public FileVO moveFile(Long fileId, Long targetFolderId, Long userId) {
        log.info("移动文件: fileId={}, targetFolderId={}, userId={}", fileId, targetFolderId, userId);

        FileEntity file = getFileById(fileId);

        // 更新文件夹ID
        file.setFolderId(targetFolderId);
        file.setUpdatedAt(LocalDateTime.now());
        fileMapper.updateById(file);

        log.info("文件移动成功: fileId={}, targetFolderId={}", fileId, targetFolderId);

        return entityToVO(file);
    }

    /**
     * 复制文件
     *
     * @param fileId         源文件ID
     * @param targetFolderId 目标文件夹ID
     * @param newName        新文件名（可选）
     * @param userId         用户ID
     * @return 新文件VO
     */
    public FileVO copyFile(Long fileId, Long targetFolderId, String newName, Long userId) {
        log.info("复制文件: fileId={}, targetFolderId={}, newName={}, userId={}",
                fileId, targetFolderId, newName, userId);

        FileEntity sourceFile = getFileById(fileId);

        // 创建新文件记录
        LocalDateTime now = LocalDateTime.now();
        FileEntity newFile = FileEntity.builder()
                .fileName(newName != null ? newName : sourceFile.getFileName() + " (副本)")
                .folderId(targetFolderId)
                .storagePath(sourceFile.getStoragePath())  // 共享存储路径
                .fileExtension(newName != null ? getFileExtension(newName) : sourceFile.getFileExtension())
                .fileSize(sourceFile.getFileSize())
                .md5Hash(sourceFile.getMd5Hash())
                .mimeType(sourceFile.getMimeType())
                .uploadedBy(userId)
                .ownerId(userId)
                .privacyLevel("shared")
                .ragflowSyncStatus("pending")
                .createdAt(now)
                .updatedAt(now)
                .build();

        fileMapper.insert(newFile);

        log.info("文件复制成功: sourceFileId={}, newFileId={}", fileId, newFile.getId());

        return entityToVO(newFile);
    }

    /**
     * 批量操作文件
     *
     * @param action         操作类型（move/copy/delete）
     * @param fileIds        文件ID列表
     * @param targetFolderId 目标文件夹ID（move/copy时需要）
     * @param userId         用户ID
     * @return 批量操作结果
     */
    public com.docmgmt.mvp.dto.BatchFileResponse batchOperation(
            String action, List<Long> fileIds, Long targetFolderId, Long userId) {
        log.info("批量操作文件: action={}, count={}, targetFolderId={}, userId={}",
                action, fileIds.size(), targetFolderId, userId);

        int successCount = 0;
        int failedCount = 0;
        List<com.docmgmt.mvp.dto.BatchFileResponse.FileOperationResult> results = new java.util.ArrayList<>();

        for (Long fileId : fileIds) {
            try {
                switch (action.toLowerCase()) {
                    case "move":
                        if (targetFolderId == null) {
                            throw new BusinessException("移动操作需要指定目标文件夹");
                        }
                        moveFile(fileId, targetFolderId, userId);
                        break;
                    case "copy":
                        if (targetFolderId == null) {
                            throw new BusinessException("复制操作需要指定目标文件夹");
                        }
                        copyFile(fileId, targetFolderId, null, userId);
                        break;
                    case "delete":
                        deleteFile(fileId, userId);
                        break;
                    default:
                        throw new BusinessException("不支持的操作类型: " + action);
                }
                successCount++;
                results.add(com.docmgmt.mvp.dto.BatchFileResponse.FileOperationResult.builder()
                        .fileId(fileId)
                        .status("success")
                        .message("操作成功")
                        .build());
            } catch (Exception e) {
                failedCount++;
                results.add(com.docmgmt.mvp.dto.BatchFileResponse.FileOperationResult.builder()
                        .fileId(fileId)
                        .status("failed")
                        .message(e.getMessage())
                        .build());
                log.error("批量操作失败: fileId={}, action={}, error={}", fileId, action, e.getMessage());
            }
        }

        log.info("批量操作完成: action={}, success={}, failed={}", action, successCount, failedCount);

        return com.docmgmt.mvp.dto.BatchFileResponse.builder()
                .successCount(successCount)
                .failedCount(failedCount)
                .results(results)
                .build();
    }

    /**
     * 增强版文件列表查询（支持关键词搜索和扩展名过滤）
     *
     * @param userId    用户ID
     * @param folderId  文件夹ID
     * @param keyword   搜索关键词
     * @param extension 文件扩展名
     * @param page      页码
     * @param pageSize  每页数量
     * @param sortBy    排序字段
     * @param sortOrder 排序方向
     * @return 分页文件列表
     */
    public PagedResult<FileVO> listFilesEnhanced(
            Long userId, Long folderId, String keyword, String extension,
            Integer page, Integer pageSize, String sortBy, String sortOrder) {
        log.info("查询文件列表（增强版）: userId={}, folderId={}, keyword={}, extension={}",
                userId, folderId, keyword, extension);

        // 参数默认值
        if (page == null || page < 1) page = 1;
        if (pageSize == null || pageSize < 1) pageSize = 20;
        if (sortBy == null || sortBy.isEmpty()) sortBy = "created_at";
        if (sortOrder == null || !sortOrder.equalsIgnoreCase("asc")) sortOrder = "desc";

        // 查询数据库（简化版：使用已有方法）
        List<FileEntity> entities;
        if (keyword != null && !keyword.isEmpty()) {
            entities = fileMapper.searchByName(keyword, userId);
        } else {
            entities = fileMapper.listFilesByCreator(userId, folderId, sortBy, sortOrder);
        }

        // 过滤扩展名
        if (extension != null && !extension.isEmpty()) {
            entities = entities.stream()
                    .filter(e -> extension.equalsIgnoreCase(e.getFileExtension()))
                    .collect(Collectors.toList());
        }

        // 手动分页
        int total = entities.size();
        int start = (page - 1) * pageSize;
        int end = Math.min(start + pageSize, total);
        List<FileEntity> pagedEntities = (start < total) ?
                entities.subList(start, end) : List.of();

        // 转换为VO
        List<FileVO> fileVOs = pagedEntities.stream()
                .map(this::entityToVO)
                .collect(Collectors.toList());

        // 构造分页响应
        return PagedResult.<FileVO>builder()
                .items(fileVOs)
                .pagination(PagedResult.Pagination.builder()
                        .page(page)
                        .pageSize(pageSize)
                        .total((long) total)
                        .totalPages((int) Math.ceil((double) total / pageSize))
                        .build())
                .build();
    }
}
